5.3.2 引用类型
Go语言里的引用类型有如下几个:切片、映射、通道、接口和函数类型。当声明上述类型的变量时,创建的变量被称作 标头 (header)值。从技术细节上说,字符串也是一种引用类型。每个引用类型创建的标头值是包含一个指向底层数据结构的指针。每个引用类型还包含一组独特的字段,用于管理底层数据结构。因为标头值是为复制而设计的,所以永远不需要共享一个引用类型的值。标头值里包含一个指针,因此通过复制来传递一个引用类型的值的副本,本质上就是在共享底层数据结构。
让我们看一下 net
包里的类型,如代码清单5-25所示。
代码清单5-25 golang.org/src/net/ip.go:第32行
32 type IP []byte
代码清单5-25展示了一个名为 IP
的类型,这个类型被声明为字节切片。当要围绕相关的内置类型或者引用类型来声明用户定义的行为时,直接基于已有类型来声明用户定义的类型会很好用。编译器只允许为命名的用户定义的类型声明方法,如代码清单5-26所示。
代码清单5-26 golang.org/src/net/ip.go:第329行到第337行
329 func (ip IP) MarshalText() ([]byte, error) {
330 if len(ip) == 0 {
331 return []byte(""), nil
332 }
333 if len(ip) != IPv4len && len(ip) != IPv6len {
334 return nil, errors.New("invalid IP address")
335 }
336 return []byte(ip.String()), nil
337 }
代码清单5-26里定义的 MarshalText
方法是用 IP
类型的值接收者声明的。一个值接收者,正像预期的那样通过复制来传递引用,从而不需要通过指针来共享引用类型的值。这种传递方法也可以应用到函数或者方法的参数传递,如代码清单5-27所示。
代码清单5-27 golang.org/src/net/ip.go:第318行到第325行
318 // ipEmptyString像ip.String一样,
319 // 只不过在没有设置ip时会返回一个空字符串
320 func ipEmptyString(ip IP) string {
321 if len(ip) == 0 {
322 return ""
323 }
324 return ip.String()
325 }
在代码清单5-27里,有一个 ipEmptyString
函数。这个函数需要传入一个 IP
类型的值。再一次,你可以看到调用者传入的是这个引用类型的值,而不是通过引用共享给这个函数。调用者将引用类型的值的副本传入这个函数。这种方法也适用于函数的返回值。最后要说的是,引用类型的值在其他方面像原始的数据类型的值一样对待。